package nec.aagent.core.service.location;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import nec.aagent.core.db.DatabaseHelper;
import nec.aagent.core.db.LocationDb;
import a.thread.ThreadUtil;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Intent;
import android.location.Location;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

import com.j256.ormlite.dao.Dao;
import com.j256.ormlite.stmt.QueryBuilder;

public class LocationTracerService extends Service {
    private static final String TAG = "LocationTracerService";
    private NotificationManager mNotificationManager;
    private ArrayBlockingQueue<Location> mSharedQueue = new ArrayBlockingQueue<Location>(5);
    private ExecutorService mExecutor = Executors.newFixedThreadPool(4);
    private boolean mRunning = false;

    private DatabaseHelper mDbHelper = null;

    public class LocationTracerServiceImpl extends ILocationTracerService.Stub {

        public void removeAll() throws RemoteException {
            ThreadUtil.logThreadSignature("LocationTracerServiceImpl");
            try {
                Dao<LocationDb, Long> locationDao = getDbHelper().getLocationDao();
                locationDao.deleteBuilder().delete();
            } catch (SQLException e) {
                Log.e(TAG, e.getMessage());
            }
        }

        public List<Location> getLocations(int maxResult)
                throws RemoteException {
            ThreadUtil.logThreadSignature("LocationTracerServiceImpl");
            try {
                Dao<LocationDb, Long> locationDao = getDbHelper().getLocationDao();

                QueryBuilder<LocationDb, Long> builder = locationDao.queryBuilder();
                if (maxResult > 0) {
                    builder = builder.limit((long) maxResult);
                }
                builder = builder.orderBy("genId", false);

                List<LocationDb> locationDbs = builder.query();
                List<Location> result = new ArrayList<Location>();
                for (LocationDb locationDb : locationDbs) {
                    result.add(LocationDb.convert(locationDb));
                }
                return result;
            } catch (SQLException e) {
                Log.e(TAG, e.getMessage());
            }
            return null;
        }
    }

    public class Producer implements Runnable {
        private Location mLocation;

        public Producer(Location location) {
            mLocation = location;
        }

        public void run() {
            try {
                mSharedQueue.put(mLocation);
                Log.v(TAG, "Producer writes: " + mLocation);
                Log.d(TAG, "Producer put location. Queue size = " + mSharedQueue.size());
            } catch (InterruptedException e) {
                Log.e(TAG, e.getMessage());
            }
        }
    }

    public class Consumer implements Runnable {
        public void run() {
            try {
                while (mRunning) {
                    Location location = mSharedQueue.take();

                    storeLocation(location);

                    Log.v(TAG, "Consumer reads: " + location);
                    Log.d(TAG, "Consumer took location. Queue size = " + mSharedQueue.size());
                }
            } catch (InterruptedException e) {
                Log.e(TAG, e.getMessage());
            }
        }
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "onCreate() called in LocationTracerService");
        mNotificationManager = (NotificationManager) getSystemService(Service.NOTIFICATION_SERVICE);
        mRunning = true;
        mExecutor.submit(new Consumer());
    }

    @Override
    public void onDestroy() {
        Log.d(TAG, "onDestroy() called in LocationTracerService");

        mRunning = false;

        mNotificationManager.cancelAll();
        mExecutor.shutdown();

        if (mDbHelper != null) {
            mDbHelper.close();
            mDbHelper = null;
        }

        super.onDestroy();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent.hasExtra(LocationManager.KEY_LOCATION_CHANGED)) {
            Bundle bundle = intent.getExtras();
            Location location = bundle.getParcelable(LocationManager.KEY_LOCATION_CHANGED);
            Log.v(TAG, "receive location:" + location);
            mExecutor.submit(new Producer(location));
        }
        return Service.START_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG, "onBind() called in LocationTracerService");
        return new LocationTracerServiceImpl();
    }

    private void storeLocation(Location location) {
        if (location == null) {
            Log.w(TAG, "Location is null, ignore it");
            return;
        }

        try {
            Dao<LocationDb, Long> dao = getDbHelper().getLocationDao();
            LocationDb locationDb = new LocationDb(location);
            dao.create(locationDb);

            Log.d(TAG, "Stored location: " + location);
        } catch (SQLException e) {
            Log.e(TAG, e.getMessage());
        }
    }

    private DatabaseHelper getDbHelper() {
        if (mDbHelper == null) {
            mDbHelper = DatabaseHelper.get(getApplicationContext());
        }
        return mDbHelper;
    }
}
